package com.express.util;

import org.apache.commons.lang3.StringUtils;
import org.apache.http.Header;
import org.apache.http.HttpEntity;
import org.apache.http.HttpStatus;
import org.apache.http.NameValuePair;
import org.apache.http.client.ClientProtocolException;
import org.apache.http.client.config.RequestConfig;
import org.apache.http.client.entity.UrlEncodedFormEntity;
import org.apache.http.client.methods.CloseableHttpResponse;
import org.apache.http.client.methods.HttpGet;
import org.apache.http.client.methods.HttpPost;
import org.apache.http.config.Registry;
import org.apache.http.config.RegistryBuilder;
import org.apache.http.conn.socket.ConnectionSocketFactory;
import org.apache.http.conn.socket.PlainConnectionSocketFactory;
import org.apache.http.conn.ssl.SSLConnectionSocketFactory;
import org.apache.http.conn.ssl.X509HostnameVerifier;
import org.apache.http.entity.ContentType;
import org.apache.http.entity.StringEntity;
import org.apache.http.entity.mime.MultipartEntityBuilder;
import org.apache.http.entity.mime.content.FileBody;
import org.apache.http.impl.client.CloseableHttpClient;
import org.apache.http.impl.client.HttpClients;
import org.apache.http.impl.conn.PoolingHttpClientConnectionManager;
import org.apache.http.message.BasicNameValuePair;
import org.apache.http.util.EntityUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Value;

import javax.net.ssl.*;
import java.io.*;
import java.security.KeyManagementException;
import java.security.NoSuchAlgorithmException;
import java.security.cert.CertificateException;
import java.security.cert.X509Certificate;
import java.util.*;


/**
 * HTTP工具类
 * 
 * @author ycye
 * 
 */
public class HttpUtils {

    private static Logger logger = LoggerFactory.getLogger(HttpUtils.class);

    @Value(value = "${isUseProxy:false}")
    private static boolean isUseProxy;

    @Value(value = "${proxyHost:proxy.licl.com.cn}")
    private static String proxyHost;

    @Value(value = "${proxyPort:8080}")
    private static int proxyPort;

    private static RequestConfig requestConfig;
    static {
        // clbhttpClient = HttpClients.createDefault();
        setRequestConfig();
    }

    private static void setRequestConfig() {
        int httpTimeOut = Integer.parseInt("30000");
        logger.info(">>> httpClient.timeOut = " + httpTimeOut);
        requestConfig =
                RequestConfig.custom().setSocketTimeout(httpTimeOut).setConnectTimeout(httpTimeOut)
                        .setConnectionRequestTimeout(httpTimeOut).build();
    }

    /**
     * HTTPGET
     * 
     * @param url
     * @param paramsMap
     * @param encoding
     * @return
     */
    public final static String httpGet(String url, Map<String, String> paramsMap, String encoding) {
        Map<String, String> headerMap = new HashMap<String, String>();
        headerMap.put("Content-Type", "application/x-www-form-urlencoded; charset=" + encoding);
        return get(url, paramsMap, headerMap, encoding, url != null && url.startsWith("https:") ? "https" : "http");
    }

    /**
     * HTTPSGET
     * 
     * @param url
     * @param paramsMap
     * @param encoding
     * @return
     */
    public final static String httpsGet(String url, Map<String, String> paramsMap, String encoding) {
        Map<String, String> headerMap = new HashMap<String, String>();
        headerMap.put("Content-Type", "application/x-www-form-urlencoded; charset=" + encoding);
        return get(url, paramsMap, headerMap, encoding, url != null && url.startsWith("https:") ? "https" : "http");
    }

    /**
     * HTTPSPOST
     * 
     * @param url
     * @param paramsMap
     * @param encoding
     * @return
     */
    public final static String httpPost(String url, Map<String, String> paramsMap, String encoding) {
        Map<String, String> headerMap = new HashMap<String, String>();
        headerMap.put("Content-Type", "application/x-www-form-urlencoded; charset=" + encoding);
        // 组装参数
        List<BasicNameValuePair> params = parseMap2BasicForm(paramsMap);
        UrlEncodedFormEntity entity = null;
        try {
            entity = new UrlEncodedFormEntity(params, encoding);
        } catch (UnsupportedEncodingException e) {
            logger.error(null, e);
        }
        return post(url, headerMap, entity, encoding, url != null && url.startsWith("https:") ? "https" : "http");
    }

    /**
     * HTTPSPOST
     * 
     * @param url
     * @param paramsMap
     * @param encoding
     * @return
     */
    public final static String httpsPost(String url, Map<String, String> paramsMap, String encoding) {
        return httpPost(url, paramsMap, encoding);
    }

    /**
     * HTTPSPOST JSON 改为根据URL的前面几个字母（协议），来进行http或https调用。
     * 
     * @param url
     * @param paramJson
     * @param encoding
     * @return
     */
    public final static String httpsPostJson(String url, String paramJson, String encoding) {
        Map<String, String> headerMap = new HashMap<String, String>();
//        headerMap.put("Content-Type", "text/json; charset=" + encoding);
        headerMap.put("Content-Type", "application/x-www-form-urlencoded; charset=" + encoding);
        // 组装参数
        StringEntity entity = null;
        try {
            entity = new StringEntity(paramJson, encoding);
        } catch (Exception e) {
            logger.error("组装参数失败" + e.getMessage(), e);
        }
        return post(url, headerMap, entity, encoding, url != null && url.startsWith("https:") ? "https" : "http");
    }
    
    /**
     * HTTPSPOST JSON 改为根据URL的前面几个字母（协议），来进行http或https调用。
     * 
     * @param url
     * @param paramJson
     * @param headDataMap
     * @return
     */
    public final static String httpsPostJson(String url, String paramJson, Map<String, String> headDataMap) {
        Map<String, String> headerMap = new HashMap<String, String>();
        headerMap.put("Content-Type", "text/json; charset=" + "utf-8");
        if (headDataMap != null && !headDataMap.isEmpty()) {
            headerMap.putAll(headDataMap);
        }
        // 组装参数
        StringEntity entity = null;
        try {
            entity = new StringEntity(paramJson, "utf-8");
        } catch (Exception e) {
            logger.error("组装参数失败" + e.getMessage(), e);

        }
        return post(url, headerMap, entity, "utf-8", url != null && url.startsWith("https:")
                ? "https"
                : "http");
    }

    /**
     * HTTPS POST FILE
     * 
     * @param url
     * @param file
     * @param encoding
     * @return
     */
    public final static String httpsPostFile(String url, File file, String fileFieldName, String encoding) {
        Map<String, String> headerMap = new HashMap<String, String>();
        // headerMap.put("Content-Type", "multipart/form-data; charset=" + encoding);
        // 组装参数
        HttpEntity entity = null;
        try {
            FileBody bin = new FileBody(file);
            entity =
                    MultipartEntityBuilder.create()
                            .addPart(StringUtils.isEmpty(fileFieldName) ? "bin" : fileFieldName, bin).build();
        } catch (Exception e) {
            logger.error(null, e);
        }
        return post(url, headerMap, entity, encoding, url != null && url.startsWith("https:") ? "https" : "http");
    }

    /**
     * HTTPS POST FILE
     * 
     * @param url
     * @param file
     * @param encoding
     * @return
     */
    public final static String httpsPostFile(String url, File file, String encoding) {
        return httpsPostFile(url, file, null, encoding);
    }

    /**
     * HTTPS POST FILE
     * 
     * @param url
     * @param file
     * @param encoding
     * @return
     */
    public final static String httpPostFile(String url, Map<String, String> paramsMap, File file, String fileFieldName,
                                            String encoding) {
        Map<String, String> headerMap = new HashMap<String, String>();
        // 组装参数
        HttpEntity entity = null;
        try {
            MultipartEntityBuilder mbuilder = MultipartEntityBuilder.create();
            if (paramsMap != null && paramsMap.size() > 0) {
                // 组装参数
                List<BasicNameValuePair> params = parseMap2BasicForm(paramsMap);
                for (NameValuePair p : params) {
                    mbuilder.addTextBody(p.getName(), p.getValue(), ContentType.TEXT_PLAIN.withCharset(encoding));
                }
            }
            // 封装文件
            FileBody bin = new FileBody(file);
            mbuilder.addPart(StringUtils.isEmpty(fileFieldName) ? "bin" : fileFieldName, bin);
            entity = mbuilder.build();
        } catch (Exception e) {
            logger.error(null, e);
        }
        return post(url, headerMap, entity, encoding, url != null && url.startsWith("https:") ? "https" : "http");
    }

    /**
     * HTTPS POST FILE
     * 
     * @param url
     * @param file
     * @param encoding
     * @return
     */
    public final static String httpPostFile(String url, Map<String, String> paramsMap, File file, String encoding) {
        return httpPostFile(url, paramsMap, file, "file", encoding);
    }

    /**
     * HTTP POST XML
     * 
     * @param url
     * @param xml
     * @param encoding
     * @return
     */
    public final static String httpPostXml(String url, String xml, String encoding) {
        Map<String, String> headerMap = new HashMap<String, String>();
        headerMap.put("Content-Type", "text/xml; charset=" + encoding);
        // 组装参数
        StringEntity entity = null;
        try {
            entity = new StringEntity(xml, encoding);
        } catch (Exception e) {
            logger.error(null, e);
        }
        return post(url, headerMap, entity, encoding, url != null && url.startsWith("https:") ? "https" : "http");
    }

    /**
     * GET
     * 
     * @param url
     * @param paramsMap
     * @param headerMap
     * @param encoding
     * @param type http or https
     * @return
     */
    private final static String get(String url, Map<String, String> paramsMap, Map<String, String> headerMap,
                                    String encoding, String type) {
        String result = "";
        logger.info("Get Data url HttpClient start  !!!!! ");
        // 组装参数
        String paramStr = "";
        Iterator iter = paramsMap.entrySet().iterator();
        while (iter.hasNext()) {
            Map.Entry entry = (Map.Entry) iter.next();
            Object key = entry.getKey();
            Object val = entry.getValue();
            paramStr += paramStr = "&" + key + "=" + val;
        }

        if (!paramStr.equals("")) {
            paramStr = paramStr.replaceFirst("&", "?");
            url += paramStr;
        }


        logger.info("Get Data url to [" + url + "] ");
        // 创建一个httpGet请求
        HttpGet request = new HttpGet(url);

        // 组装header参数
        Iterator iterHeader = headerMap.entrySet().iterator();
        while (iterHeader.hasNext()) {
            Map.Entry entry = (Map.Entry) iterHeader.next();
            request.setHeader(String.valueOf(entry.getKey()), String.valueOf(entry.getValue()));
        }

        // 创建一个htt客户端
        CloseableHttpClient httpClient = null;
        CloseableHttpResponse httpResponse = null;
        try {
            // 创建一个htt客户端
            httpClient = "https".equals(type) ? getHttpsClient() : getHttpClient();
            // -------------2015-9-29 start

            request.setConfig(requestConfig);

            // -------------2015-9-29 end
            // 接受客户端发回的响应
            httpResponse = (CloseableHttpResponse) httpClient.execute(request);
            int statusCode = httpResponse.getStatusLine().getStatusCode();
            if (statusCode == HttpStatus.SC_OK) {
                // 得到客户段响应的实体内容
                result = EntityUtils.toString(httpResponse.getEntity(), encoding);
            } else {
                logger.error("URL:" + url + "\tStatusCode:" + statusCode);
            }
            logger.info("get request success! ");
        } catch (Exception e) {
            logger.error("https request timeout!", e);
        } finally {
            // -------- 2015-9-29 是否链接 start
            // 判断request是否等于null
            if (null != httpResponse) {
                try {
                    // 释放链接
                    httpResponse.close();
                    logger.info("close http response");
                } catch (Exception e) {
                    logger.error("close http response fail", e);
                }
            }
            if (null != request) {
                try {
                    // 释放链接
                    request.abort();
                } catch (Exception e) {
                    logger.error("close request fail", e);
                }
            }
            if (null != httpClient) {
                try {
                    httpClient.close();
                } catch (IOException e) {
                    // TODO Auto-generated catch block
                    logger.error("close httpClient fail", e);
                }
            }
            // -------- 2015-9-29 end
        }
        return result;
    }

    /**
     * POST
     * 
     * @param url
     * @param headerMap
     * @param encoding
     * @param type http or https
     * @return
     */
    private final static String post(String url, Map<String, String> headerMap, HttpEntity requestEntity,
                                     String encoding, String type) {
        logger.info("Post Data HttpClient url start  !!!!! ");
        String result = "";

        // 创建一个httpGet请求
        HttpPost request = null;

        logger.info("Post Data to [" + url + "] ");

        // 创建一个htt客户端
        CloseableHttpClient httpClient = null;
        CloseableHttpResponse httpResponse = null;
        try {
            // 创建一个httpGet请求
            request = new HttpPost(url);

            // 组装header参数
            Iterator<?> iter = headerMap.entrySet().iterator();
            while (iter.hasNext()) {
                Map.Entry entry = (Map.Entry) iter.next();
                request.setHeader(String.valueOf(entry.getKey()), String.valueOf(entry.getValue()));
            }
            // 设置参数
            request.setEntity(requestEntity);


            // 创建一个htt客户端
            httpClient = "https".equals(type) ? getHttpsClient() : getHttpClient();

            // -------------2015-9-29 start
            request.setConfig(requestConfig);

            // -------------2015-9-29 end
            // 接受客户端发回的响应
            httpResponse = httpClient.execute(request);
            int statusCode = httpResponse.getStatusLine().getStatusCode();
            if (statusCode == HttpStatus.SC_OK) {
                // 得到客户段响应的实体内容
                result = EntityUtils.toString(httpResponse.getEntity(), encoding);
            } else if (HttpStatus.SC_BAD_GATEWAY == statusCode) { // 调用微信群发接口有可能返回502 解决办法当成成功返回
                logger.info("Post Data to [" + url + "] " + statusCode + ":content:"
                        + EntityUtils.toString(httpResponse.getEntity(), encoding));
                result = "{\"errcode\":0,\"errmsg\":\"send job submission success\",\"msg_id\":0}";
            }
            logger.info("Post request success!!!!! ");
        } catch (Exception e) {
            logger.info("https request timeout!!!!!", e);
        } finally {
            // -------- 2015-9-29 是否链接 start
            // 判断request是否等于null
            if (null != httpResponse) {
                try {
                    // 释放链接
                    httpResponse.close();
                    logger.info("close http response");
                } catch (Exception e) {
                    logger.error("close http response fail", e);
                }
            }
            if (null != request) {
                try {
                    // 释放链接
                    request.abort();
                } catch (Exception e) {
                    logger.error("close request fail", e);
                }
            }
            if (null != httpClient) {
                try {
                    httpClient.close();
                } catch (IOException e) {
                    // TODO Auto-generated catch block
                    logger.error("close httpClient fail", e);
                }
            }
            // -------- 2015-9-29 end
        }


        return result;
    }

    /**
     * 封装MAP格式的参数到BasicNameValuePair中
     * 
     * @param paramsMap
     * @return
     */
    private static final List<BasicNameValuePair> parseMap2BasicForm(Map<String, String> paramsMap) {
        List<BasicNameValuePair> params = null;
        if (paramsMap != null && paramsMap.size() > 0) {
            Iterator<String> it = paramsMap.keySet().iterator();
            params = new ArrayList<BasicNameValuePair>();
            String keyTmp = null;
            while (it.hasNext()) {
                keyTmp = (String) it.next();
                params.add(new BasicNameValuePair(keyTmp, paramsMap.get(keyTmp)));
            }
        }
        return params;
    }
    /**
     * 绕过验证
     *
     * @return
     * @throws NoSuchAlgorithmException
     * @throws KeyManagementException
     */
    public static SSLContext createIgnoreVerifySSL() throws NoSuchAlgorithmException, KeyManagementException {
        SSLContext sc = SSLContext.getInstance("SSLv3");

        // 实现一个X509TrustManager接口，用于绕过验证，不用修改里面的方法
        X509TrustManager trustManager = new X509TrustManager() {
            @Override
            public void checkClientTrusted(
                    java.security.cert.X509Certificate[] paramArrayOfX509Certificate,
                    String paramString) throws CertificateException {
            }

            @Override
            public void checkServerTrusted(
                    java.security.cert.X509Certificate[] paramArrayOfX509Certificate,
                    String paramString) throws CertificateException {
            }

            @Override
            public java.security.cert.X509Certificate[] getAcceptedIssuers() {
                return null;
            }
        };

        sc.init(null, new TrustManager[] { trustManager }, null);
        return sc;
    }
    /**
     * 取已配置的HttpsClient
     * 
     * @return
     * @throws NoSuchAlgorithmException
     * @throws KeyManagementException
     */
    private final static CloseableHttpClient getHttpsClient() throws NoSuchAlgorithmException, KeyManagementException {
        CloseableHttpClient httpsClient = null;
        // 创建默认的httpClient实例
        X509TrustManager xtm = new X509TrustManager() { // 创建TrustManager
                    public void checkClientTrusted(X509Certificate[] chain, String authType)
                            throws CertificateException {}

                    public void checkServerTrusted(X509Certificate[] chain, String authType)
                            throws CertificateException {}

                    public X509Certificate[] getAcceptedIssuers() {
                        return null;
                    }
                };
        X509HostnameVerifier hostnameVerifier = new X509HostnameVerifier() {
            public boolean verify(String hostname, SSLSession session) {
                return false;
            }

            public void verify(String arg0, SSLSocket arg1) throws IOException {}

            @Override
            public void verify(String arg0, X509Certificate arg1) throws SSLException {}

            @Override
            public void verify(String arg0, String[] arg1, String[] arg2) throws SSLException {}
        };
        SSLContext ctx;
        try {
            //
            //ctx = SSLContext.getInstance("TLS");
            // 使用TrustManager来初始化该上下文，TrustManager只是被SSL的Socket所使用
            //ctx.init(null, new TrustManager[] {xtm}, new java.security.SecureRandom());
            // 创建SSLSocketFactory
            //SSLConnectionSocketFactory socketFactory = new SSLConnectionSocketFactory(ctx, hostnameVerifier);
            // 通过SchemeRegistry将SSLSocketFactory注册到我们的HttpClient上
            //httpsClient = HttpClients.custom().setSSLSocketFactory(socketFactory).build();\

            //采用绕过验证的方式处理https请求
            ctx = createIgnoreVerifySSL();
            //设置协议http和https对应的处理socket链接工厂的对象
            Registry<ConnectionSocketFactory> socketFactoryRegistry = RegistryBuilder.<ConnectionSocketFactory>create()
                    .register("http", PlainConnectionSocketFactory.INSTANCE)
                    .register("https", new SSLConnectionSocketFactory(ctx))
                    .build();
            PoolingHttpClientConnectionManager connManager = new PoolingHttpClientConnectionManager(socketFactoryRegistry);
            HttpClients.custom().setConnectionManager(connManager);
            //创建自定义的httpclient对象
            httpsClient = HttpClients.custom().setConnectionManager(connManager).build();
            //CloseableHttpClient client = HttpClients.createDefault();
        } catch (Exception e) {
            logger.error("getHttpsClient error: ", e);
        }
        return httpsClient;
    }

    /**
     * http get string from remote machine
     * 
     * @param url
     * @return
     * @throws IOException
     * @throws ClientProtocolException
     */
    @SuppressWarnings({"resource"})
    public static String getNetString(String url) throws Exception {
        String result = "";
        HttpGet get = null;
        CloseableHttpResponse httpResponse = null;
        try {
            CloseableHttpClient httpclient = getHttpClient();
            get = new HttpGet(url);
            httpResponse = (CloseableHttpResponse) httpclient.execute(get);
            int statusCode = httpResponse.getStatusLine().getStatusCode();
            if (statusCode == HttpStatus.SC_OK) {
                // 得到客户段响应的实体内容
                result = EntityUtils.toString(httpResponse.getEntity(), "utf-8");
            }

        } catch (Exception e) {
            logger.error("getNetString http get timeout !!", e);
            throw e;
        } finally {
            if (null != httpResponse) {
                try {
                    // 释放链接
                    httpResponse.close();
                    logger.info("close http response");
                } catch (Exception e) {
                    logger.error("close http response fail", e);
                    throw e;
                }
            }
            if (null != get) {
                try {
                    // 释放链接
                    get.abort();
                } catch (Exception e) {
                    logger.error("close request fail", e);
                    throw e;
                }
            }
        }

        return result;
    }

    /**
     * getRemoteFile 下载文件的远程调用函数
     * 
     * @param urlPath
     * @param savePath
     * @return
     */
    public static void getRemoteFile(String urlPath, String savePath) throws Exception {

        // 下载网络文件
        int bytesum = 0;
        int byteread = 0;
        InputStream instream = null;
        FileOutputStream fs = null;

        CloseableHttpClient client = null;
        HttpGet get = null;
        CloseableHttpResponse response = null;

        try {
            // 默认的client类。
            client = urlPath.startsWith("https") ? getHttpsClient() : getHttpClient();
            // 设置为get取连接的方式.
            get = new HttpGet(urlPath);
            // 得到返回的response.
            response = (CloseableHttpResponse) client.execute(get);
            // 得到返回的client里面的实体对象信息.
            HttpEntity entity = response.getEntity();
            if (entity != null) {
                // 得到返回的主体内容.
                instream = entity.getContent();
                Header contentType = entity.getContentType();
                String ctt = contentType.getValue();
                if (ctt != null && ctt.startsWith("text/plain")) {// 返回的为文本时，判断为服务器返回的错误消息
                                                                  // "text/plain"或"text/plain;charset=UTF-8"等
                    throw new FileNotFoundException(XmlUtil.inputStream2String(instream));
                }
                // START 检查目录是否存在。不存在时，需要新建目录。
                File outFile = new File(savePath);
                if (outFile != null) {
                    if (outFile.getParentFile() != null && !outFile.getParentFile().exists()) {
                        outFile.getParentFile().mkdirs();
                    }
                }
                // END
                fs = new FileOutputStream(outFile);
                byte[] buffer = new byte[1204];

                while ((byteread = instream.read(buffer)) != -1) {
                    bytesum += byteread;
                    fs.write(buffer, 0, byteread);
                }

            }
        } catch (Exception e) {
            logger.info("getRemoteFile http request timeout!!!!!", e);
            throw e;
        } finally {
            if (null != instream) {
                instream.close();
            }
            if (null != fs) {
                fs.close();
            }
            if (null != response) {
                try {
                    // 释放链接
                    response.close();
                    logger.info("close http response");
                } catch (Exception e) {
                    logger.error("close http response fail", e);
                    throw e;
                }
            }
            if (null != get) {
                try {
                    // 释放链接
                    get.abort();
                } catch (Exception e) {
                    logger.error("close request fail", e);
                    throw e;
                }
            }
        }

    }


    public static CloseableHttpClient getHttpClient() {
        CloseableHttpClient clbhttpClient = HttpClients.createDefault();
        return clbhttpClient;
    }


}
